<?php
class table extends htmlwidget {
		public $rows = array();
		public $rowattribs = array();
		public $colattribs = array();
		public $cellattribs = array();
		public $fullrows = array();
		public $rowspans = array();
		public $numcellstoomit = array();
		public $numcolumns;
		public $numrows = 0;
		public $currentrownum = 0;
		public $currentcolnum = 0;
		public $caption = '';
		public $headandbodytags = 0;
		
	
		public function __construct(){
			$this->html = '';
			$this->numrows = 0;
			$this->numcolumns = 0;
			$this->validAttributes[] = 'width';
			$this->validAttributes[] = 'align';
			$this->validAttributes[] = 'valign';
			$this->validAttributes[] = 'background';
			$this->validAttributes[] = 'bgcolor';
			$this->validAttributes[] = 'border';
			$this->validAttributes[] = 'bordercolor';
			$this->validAttributes[] = 'bordercolordark';
			$this->validAttributes[] = 'bordercolorlight';
			$this->validAttributes[] = 'cellpadding';
			$this->validAttributes[] = 'cellspacing';
			$this->validAttributes[] = 'rules';
		}
		
		public function setheadandbodytags($value){
			if ($value){
				$this->headandbodytags = 1;
			} else {
				$this->headandbodytags = 0;
			}
		}
	
		public function row(){
			$this->currentcolnum = 0;
			
			//check to see if we already have an array in the current position.
			//if we do, advance to the next position
			$wehaverow = in_array($this->currentrownum, array_keys($this->rows));
			$isrowanarray = (isset($this->rows[$this->currentrownum])) && (is_array($this->rows[$this->currentrownum]));
			if ( $wehaverow && $isrowanarray){
				$this->currentrownum++;
			}
						
			$this->rows[$this->currentrownum] = array();
	
			$numcells = func_num_args();
			if ( $numcells > $this->numcolumns){
				$this->numcolumns = $numcells;
			}
			$cells = func_get_args();
	
			$arraysize = count($cells);
			foreach ( $cells as $key => $value){
				if ( (is_null($value))){
					$this->rows[$this->currentrownum][] = '';
				} elseif ( is_scalar($value)){
					if ( strlen($value) > 0){
						$this->rows[$this->currentrownum][] = $value;
					} else {
						$this->rows[$this->currentrownum][] = '';
					}
				} elseif ( is_array($value)){
					$this->rows[$this->currentrownum][] = join(" ", $value);
				} elseif ( $value instanceof htmlwidget){
					$this->rows[$this->currentrownum][] = $value->getHtml();
				} elseif (is_object($value) && method_exists($value , '__toString')){
					$this->rows[$this->currentrownum][] = $value->__toString();
				} else {
					$this->rows[$this->currentrownum][] = '';
				}
			}
			$this->numrows++;
			$this->currentrownum++;
		}
		
		//adds a td that spans the whole row
		public function fullrow($value){
			$this->currentcolnum = 0;
			
			//check to see if we already have an array in the current position.
			//if we do, advance to the next position
			$wehaverow = in_array($this->currentrownum, array_keys($this->rows));
			$isrowanarray = (isset($this->rows[$this->currentrownum])) && (is_array($this->rows[$this->currentrownum]));
			if ( $wehaverow && $isrowanarray){
				$this->currentrownum++;
			}
			
			$this->rows[$this->currentrownum] = array();
	
			$this->fullrows[] = $this->currentrownum;
			
			$numcells = 1;
			if ( $numcells > $this->numcolumns){
				$this->numcolumns = $numcells;
			}
	
			if ( is_null($value) ){
				$this->rows[$this->currentrownum][] = '';
			} elseif ( is_scalar($value)){
				if ( strlen($value) == 0 ){
					$this->rows[$this->currentrownum][] = '';
				} else {
					$this->rows[$this->currentrownum][] = $value;
				}
			} elseif ( is_array($value)){
				$this->rows[$this->currentrownum][] = join(" ", $value);
			} elseif ( $value instanceof htmlwidget){
				$this->rows[$this->currentrownum][] = $value->getHtml();
			} elseif (is_object($value) && method_exists($value , '__toString')){
				$this->rows[$this->currentrownum][] = $value->__toString();
			} else {
				$this->rows[$this->currentrownum][] = '';
			}
			$this->numrows++;
			$this->currentrownum++;
		}
		
	
		public function newrow(){
			$this->currentcolnum = 0;
			$wehaverow = in_array($this->currentrownum, array_keys($this->rows));
			$isrowanarray = (isset($this->rows[$this->currentrownum])) && (is_array($this->rows[$this->currentrownum]));
			if ( $wehaverow && $isrowanarray){
				$this->currentrownum++;					
				$this->rows[$this->currentrownum] = array();
				$this->numrows++;				
			} else {			
				$this->rows[$this->currentrownum] = array();	
				if ( $this->numrows == 0){
					$this->numrows++;
				}
			}		
		}
		
		public function col($value){
			if ( ! in_array($this->currentrownum, array_keys($this->rows) ) ){
				$this->rows[$this->currentrownum] = array();				
				$this->numrows++;
				$numcells = 1;
				if ( $numcells > $this->numcolumns){
					$this->numcolumns = $numcells;
				}				
			}
			
			if ( (is_null($value))){
				$this->rows[$this->currentrownum][] = '';
			} elseif ( is_scalar($value)){
				if (strlen($value) == 0 ){
					$this->rows[$this->currentrownum][] = '';
				} else {
					$this->rows[$this->currentrownum][] = $value;
				}
			} elseif ( is_array($value)){
				$this->rows[$this->currentrownum][] = join(" ", $value);
			} elseif ( $value instanceof htmlwidget){
				$this->rows[$this->currentrownum][] = $value->getHtml();
			} elseif (is_object($value) && method_exists($value , '__toString')){
				$this->rows[$this->currentrownum][] = $value->__toString();
			} else {
				$this->rows[$this->currentrownum][] = '';
			}	
			
			if ( count($this->rows[$this->currentrownum]) > $this->numcolumns){
				$this->numcolumns = count($this->rows[$this->currentrownum]);
			}
			$this->currentcolnum++;
		}
		
		public function setRowAttrib($attrname, $attrvalue,$rownum=null){
			$this->setRowAttribs($attrname, $attrvalue, $rownum);
		}
				
		public function setRowAttribs($attrname, $attrvalue, $rownum=null){
			if( is_null($rownum) || ( strlen($rownum) == 0) ){
				$rownum = $this->currentrownum;
			} elseif ( $rownum == -1 ){
				$rownum = $this->currentrownum - 1;
			}
			$this->rowattribs[$rownum][$attrname] = $attrvalue;			
		}
	
		public function setColAttrib($colnum, $name , $value){
			$this->colattribs[$colnum][$name] = $value;
		}		
				
		public function setCellAttrib($attribname, $attribvalue, $rownum='', $column = '') {
			if (strlen($rownum) == 0 || is_null($rownum)){
				$rownum = $this->currentrownum;
			}
			if (strlen($column) == 0 || is_null($column)){
				$column = $this->currentcolnum;
			}
			$this->cellattribs[$rownum][$column][$attribname] = $attribvalue;
						
			if ( strtolower($attribname) == 'rowspan'){
				$this->rowspans[$rownum][$column] = $attribvalue;
			}
		}
	
		public function zebra($color, $altcolor){
			for ( $i = 0; $i <= count($this->rows); $i++){
				if ( $i % 2 == 0){
					$this->setRowAttrib('bgcolor', $color, $i);
				} else {
					$this->setRowAttrib('bgcolor', $altcolor, $i);
				}
			}
		}	
		
		public function zebraEven($color, $altcolor){
			$counter = 0;
			for ( $i = 0; $i <= count($this->rows); $i++){
				if ( $i % 2 == 0) {
					continue;
				} else {
					$counter++;
					if (  $counter % 2 == 0){
						$this->setRowAttrib('bgcolor', $color, $i);
					} else {
						$this->setRowAttrib('bgcolor', $altcolor, $i);
					}
				}
			}
		}		
		
		
		public function addborders(){
			$this->setAttrib('rules', 'ALL');
			$this->setAttrib('cellpadding', '1');
			$this->setAttrib('border', '1');
		}
		
		public function setCaption($caption){
			$this->caption = $caption;
		}
		
		public function calculaterowspan(){
			/*
			$table->rowspans[0][1] = 2
			$table->rowspans[0][9] = 2
			$table->numcellstoomit[0] = 0
			$table->numcellstoomit[1] = 0
			*/
			if ( count($this->rowspans)){
				foreach ( $this->rowspans as $rownum => $arowspan){	
					foreach ( $arowspan as $colnum => $numcellstoomit){			
	
						
						$cells = $numcellstoomit - 1;
						$currentrow = $rownum + 1; //start adjusting in the next row
						while ( $cells > 0){					
							if ( isset($this->numcellstoomit[$currentrow]) ){
								$this->numcellstoomit[$currentrow]++;
							} else {
								$this->numcellstoomit[$currentrow] = 1;	
							}
							$cells--;
							$currentrow++;						
						}
						
						
					}
				}
			}
			foreach ($this->rows as $rownum => $row){
				if ( ! isset($this->numcellstoomit[$rownum])){
					$this->numcellstoomit[$rownum]= 0;
				}
			}
		}
		
		
		public function __toString(){
			
			//calculate the number of cells that need to be omitted if there are any rowspans
			$this->calculaterowspan();
			
			//make sure we have set the num of columns to the right value
			foreach($this->rows as $row){
				if ( $this->numcolumns < count($row)){
					$this->numcolumns = count($row);
				}
			}
			
			//add <table> tag
			$this->html = "\n<table";
			foreach ( $this->attributes as $key => $value){
				if ( (! empty($value)) || (strlen($value)) ){
					$this->html .= ' ' . $key . '="' . $value . '"';
				}
			}
			
			$this->html .= ">\n";
	
			if ( ! empty($this->caption)){
				$this->html .= '<caption>';
				$this->html .= '<em>'. $this->caption . '</em>';
				$this->html .= '</caption>' . "\n";
			}
			
			//add all table rows
			foreach ( $this->rows as $rownum => $row){
				
				if ( $rownum == 0){
					if ($this->headandbodytags){
						$this->html .= "<thead>\n";
					}					
				}
				
				//opening <tr> tag and its attributes if any
				$rowattr = '';
				if ( isset($this->rowattribs[$rownum])){
					if( is_array($this->rowattribs[$rownum] )){
						foreach ( $this->rowattribs[$rownum] as $k => $v){
							$rowattr .= $k . '="' . $v . '" ';
						}
					}
				}
				$rowattr = trim($rowattr);
				if ( strlen($rowattr)){
					$rowattr = ' ' . $rowattr;
				}
				$this->html .= "\t<tr" . $rowattr . ">\n";
	
	
				//<td> tags for this row
				$tdcount = 0;
				foreach ( $row as $cellnum => $cell){
					$attr = '';
					if ( isset($this->cellattribs[$rownum][$cellnum])){
						if ( is_array($this->cellattribs[$rownum][$cellnum]) ){
							foreach ( $this->cellattribs[$rownum][$cellnum] as $k => $v ) {
								$attr .= ' ' . $k . '="' . $v . '"';
							}
						}
					}
	
					if ( isset($this->colattribs[$tdcount])){
						if ( is_array($this->colattribs) ){
							foreach ( $this->colattribs[$tdcount] as $k => $v ) {
								$attr .= ' ' . $k . '="' . $v . '"';
							}
						}
					}
					
					if ( in_array( $rownum, $this->fullrows)){
						$attr .= ' colspan="' . $this->numcolumns . '"';				
					} 
					
					$attr = trim($attr);
					if ( strlen($attr)){
						$attr = ' ' . $attr;
					}
					if ( $rownum == 0 ){
						if ( $this->headandbodytags){
							$this->html .= "\t\t<th" . $attr . ">" . $cell . "</th>\n";
						} else {
							$this->html .= "\t\t<td" . $attr . ">" . $cell . "</td>\n";
						}
					} else {
						$this->html .= "\t\t<td" . $attr . ">" . $cell . "</td>\n";	
					}
					$attr = '';
					$tdcount++;
				}
				
				if ( in_array( $rownum, $this->fullrows)){
					//closing tag and continue
					$this->html .= "\t</tr>\n";				
					continue;
				} 
				
				//adjust for colspans
				$numcolspans = 0;
				if ( isset($this->cellattribs[$rownum]) && is_array($this->cellattribs[$rownum])){
					foreach ( $this->cellattribs[$rownum] as $columnumber => $cellattributes){						
						foreach ( $cellattributes as $attribname => $attribvalue){
							if ( $attribname == 'colspan'){
								$numcolspans += $this->cellattribs[$rownum][$columnumber][$attribname] -1;								
							}
						}
					}													
				}
				
				while( $this->numcolumns &&  ($tdcount < ($this->numcolumns - $this->numcellstoomit[$rownum] - $numcolspans) )){
					$this->html .= "\t\t<td>&nbsp;</td>\n";
					$tdcount++;
				}
	
				
				//closing </tr> tag
				$this->html .= "\t</tr>\n";
				
				if ( $rownum == 0){
					if ($this->headandbodytags){
						$this->html .= "</thead>\n<tbody>\n";
					}					
				}	
			}
	
			if ($this->headandbodytags){
				$this->html .= "</tbody>\n";
			}				
			//add </table> tag
			$this->html .=  "</table>\n";
			return $this->html;
		}
	
		public function rowcount(){
			return $this->numrows;
	}
	
	public function setwidth($width){
		$this->setAttrib('width', $width);
	}
	
	public function clear(){
		parent::clear();
		$this->rowattribs = array();
		$this->cellattribs = array();
		$this->colattribs = array();		
	}
	
	//this function works only if jquery is included in the current page
	//and the sortable table plugin is included in jquery	
	public function getSortableTable(){
		$this->setheadandbodytags(1);
		if ( empty($this->attributes['id'] ))	{
			$this->attributes['id'] = $this->randomstring();
		}

		$ret = $this->__toString();
		$ret .= '<script language="JavaScript" type="text/javascript">' . "\n";
		$ret .= '$(document).ready(function() ' . "\n";
	    $ret .= "{ \n";
	    $ret .= '    $("#' . $this->attributes['id'] . '").tablesorter(); ' . "\n";
	    $ret .= ' });' . "\n"; 
	    $ret .= "</script>	\n";
	    return $ret;
	}
	
	public function center(){
		$this->setAttrib('align', 'center');
	}
	
	public function transpose(){
		if ( $this->numcolumns < 1 && $this->numrows < 1) {
			trigger_error("Can not transpose an empty table", E_USER_ERROR);
			return 0;
		}
		if ( $this->numcolumns == 1 && $this->numrows == 1) {
			return 1; //result would be the same as the original table
		}
		
		$this->clear();
		if ( $this->numrows == 0){
			throw new Exception('Table has no rows.');
		}
		if ( $this->numcolumns == 0){
			throw new Exception('Table nas no columns');
		}

		$temparr = array();
		for ( $i = 0; $i <= $this->numcolumns - 1; $i++){
			for ( $j = 0; $j <= $this->numrows - 1; $j++){
				if( isset($this->rows[$j][$i])){
					$temparr[$i][$j] = $this->rows[$j][$i];
				} else {
					$temparr[$i][$j] = '';
				}
			}
		}
		$this->rows = $temparr;
	}
	
}
?>